package controller

import (
	"crypto/rand"
	"encoding/base64"
	"net/http"
	"strings"
	"time"

	"gitee.com/huangxinbo/xcas/internal/app/base"
	"gitee.com/huangxinbo/xcas/internal/constants"
	"gitee.com/huangxinbo/xcas/internal/model"
	"gitee.com/huangxinbo/xcas/internal/service"
	"gitee.com/huangxinbo/xcas/pkg/response"
	"github.com/gin-contrib/location"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
)

type OAuthController struct {
	base    *base.Base
	service *service.Service
}

func NewOAuthController(base *base.Base, service *service.Service) *OAuthController {
	return &OAuthController{
		base:    base,
		service: service,
	}
}

func (c *OAuthController) RegisterRoutes(router *gin.Engine) {
	oauthGroup := router.Group("/api/oauth")
	{
		oauthGroup.POST("/login", c.OAuthLogin)
		oauthGroup.GET("/callback/:provider", c.OAuthCallback)
		oauthGroup.GET("/providers", c.GetOAuthProviders)
		oauthGroup.POST("/unlink", c.UnlinkOAuthProvider)
		oauthGroup.POST("/set-primary-email", c.SetPrimaryEmail)
	}
}

// generateState 生成随机的 state 参数
func (c *OAuthController) generateState() (string, error) {
	bytes := make([]byte, 32)
	if _, err := rand.Read(bytes); err != nil {
		return "", err
	}
	return base64.URLEncoding.EncodeToString(bytes), nil
}

// OAuthLogin OAuth 登录
//
//	@Summary		OAuth Login
//	@Description	Start OAuth login process
//	@Tags			oauth
//	@Accept			json
//	@Produce		json
//	@Param			request	body		model.OAuthLoginRequest		true	"OAuth login params"
//	@Success		200		{object}	model.OAuthLoginResponse	"OAuth login started"
//	@Router			/api/oauth/login [post]
func (c *OAuthController) OAuthLogin(ctx *gin.Context) {
	var req model.OAuthLoginRequest
	if err := ctx.ShouldBindJSON(&req); err != nil {
		response.Error[any](ctx, response.INVALID_PARAMS, c.base.I18n.T("Invalid parameters"), nil)
		return
	}

	// 生成 state 参数
	state, err := c.generateState()
	if err != nil {
		c.base.Logger.Error("Failed to generate state", zap.Error(err))
		response.Error[any](ctx, response.ERROR, c.base.I18n.T("Internal server error"), nil)
		return
	}

	// 生成认证 URL
	authURL, err := c.service.OAuthService.GenerateAuthURL(ctx, req.Provider, state)
	if err != nil {
		c.base.Logger.Error("Failed to generate auth URL",
			zap.String("provider", string(req.Provider)),
			zap.Error(err))
		response.Error[any](ctx, response.ERROR, c.base.I18n.T("OAuth provider not configured"), nil)
		return
	}

	// 将 state 存储在 Redis 中，有效期 10 分钟
	err = c.base.RedisClient.Set(ctx, "oauth_state:"+state, "valid", 10*time.Minute).Err()
	if err != nil {
		c.base.Logger.Error("Failed to store state in Redis", zap.Error(err))
		response.Error[any](ctx, response.ERROR, c.base.I18n.T("Internal server error"), nil)
		return
	}

	responseData := model.OAuthLoginResponseData{
		AuthURL: authURL,
	}

	response.Success(ctx, c.base.I18n.T("OAuth login started"), responseData)
}

// OAuthCallback Provider OAuth 回调
//
//	@Summary		GitHub OAuth Callback
//	@Description	GitHub OAuth callback endpoint
//	@Tags			oauth
//	@Accept			json
//	@Produce		json
//	@Param			provider	path		string	true	"Privider: github, gitee"
//	@Param			code		query		string	true	"Authorization code"
//	@Param			state		query		string	true	"State parameter"
//	@Success		302			{string}	string	"Redirect to service"
//	@Router			/api/oauth/callback/{provider} [get]
func (c *OAuthController) OAuthCallback(ctx *gin.Context) {
	provider := ctx.Param("provider")
	if provider == "" {
		response.Error[any](ctx, response.INVALID_PARAMS, c.base.I18n.T("Provider is required"), nil)
		return
	}
	switch provider {
	case "github":
		c.handleOAuthCallback(ctx, model.OAuthProviderGitHub)
	case "gitee":
		c.handleOAuthCallback(ctx, model.OAuthProviderGitee)
	default:
		response.Error[any](ctx, response.INVALID_PARAMS, c.base.I18n.T("Provider is invalid"), nil)
		return
	}
}

// handleOAuthCallback 处理 OAuth 回调
func (c *OAuthController) handleOAuthCallback(ctx *gin.Context, provider model.OAuthProvider) {
	code := ctx.Query("code")
	state := ctx.Query("state")

	if code == "" || state == "" {
		c.base.Logger.Error("Missing code or state parameter",
			zap.String("provider", string(provider)))
		c.redirectToError(ctx, "missing_parameters")
		return
	}

	// 验证 state 参数
	valid, err := c.base.RedisClient.Get(ctx, "oauth_state:"+state).Result()
	if err != nil || valid != "valid" {
		c.base.Logger.Error("Invalid state parameter",
			zap.String("provider", string(provider)),
			zap.String("state", state))
		c.redirectToError(ctx, "invalid_state")
		return
	}

	// 删除已使用的 state
	c.base.RedisClient.Del(ctx, "oauth_state:"+state)

	// 交换访问令牌
	accessToken, err := c.service.OAuthService.ExchangeCodeForToken(ctx, provider, code)
	if err != nil {
		c.base.Logger.Error("Failed to exchange code for token",
			zap.String("provider", string(provider)),
			zap.Error(err))
		c.redirectToError(ctx, "token_exchange_failed")
		return
	}

	// 获取用户信息
	oauthUser, err := c.service.OAuthService.GetUserInfo(ctx, provider, accessToken)
	if err != nil {
		c.base.Logger.Error("Failed to get user info",
			zap.String("provider", string(provider)),
			zap.Error(err))
		c.redirectToError(ctx, "user_info_failed")
		return
	}

	// 检查 oauthUser 是否为 nil
	if oauthUser == nil {
		c.base.Logger.Error("OAuth user info is nil",
			zap.String("provider", string(provider)))
		c.redirectToError(ctx, "user_info_failed")
		return
	}

	// 检查用户是否已经登录（通过 TGC Cookie）
	tgcCookie, err := ctx.Cookie(constants.TGC)
	var currentUserID string

	if err == nil && tgcCookie != "" {
		// 用户已登录，获取当前用户ID
		tgt, err := c.service.CasService.GetTGTByID(ctx, tgcCookie)
		if err == nil && tgt != nil {
			currentUserID = tgt.PrincipalID.String()
		}
	}

	// 如果用户已登录，执行绑定操作；否则执行登录操作
	if currentUserID != "" {
		// 绑定 OAuth 提供商到当前用户
		err := c.service.OAuthService.LinkOAuthToCurrentUser(ctx, currentUserID, provider, oauthUser)
		if err != nil {
			c.base.Logger.Error("Failed to link OAuth provider",
				zap.String("provider", string(provider)),
				zap.String("current_user_id", currentUserID),
				zap.Error(err))
			c.redirectToProfileWithError(ctx, "binding_failed")
			return
		}

		// 绑定成功，重定向到个人资料页面
		c.redirectToProfileWithSuccess(ctx)
		return
	} else {
		// 用户未登录，执行登录操作
		user, err := c.service.OAuthService.FindOrCreateUser(ctx, oauthUser)
		if err != nil {
			// 安全地记录错误信息，此时 oauthUser 肯定不为 nil
			c.base.Logger.Error("Failed to find or create user",
				zap.String("provider", string(provider)),
				zap.String("email", oauthUser.Email),
				zap.Error(err))
			c.redirectToError(ctx, "user_creation_failed")
			return
		}

		// 创建TGT
		userUUID, err := uuid.Parse(user.ID)
		if err != nil {
			c.base.Logger.Error("Failed to parse user ID as UUID", zap.Error(err))
			c.redirectToError(ctx, "internal_error")
			return
		}

		tgt, err := c.service.CasService.CreateTGT(ctx, userUUID, false)
		if err != nil {
			c.base.Logger.Error("Failed to create TGT", zap.Error(err))
			c.redirectToError(ctx, "tgt_creation_failed")
			return
		}

		// 设置TGC Cookie
		ctx.SetCookie(constants.TGC, tgt.ID, int(time.Until(tgt.ExpiredAt).Seconds()), "/", "", true, true)

		// 获取 service 参数
		service := ctx.Query("service")
		if service == "" {
			url := location.Get(ctx)
			if url == nil {
				c.base.Logger.Error("Failed to get request URL for service")
				c.redirectToError(ctx, "internal_error")
				return
			}
			service = url.Scheme + "://" + url.Host + "/cas/profile"
		}

		// 创建ST
		st, err := c.service.CasService.CreateST(ctx, tgt.ID, service)
		if err != nil {
			c.base.Logger.Error("Failed to create ST", zap.Error(err))
			c.redirectToError(ctx, "st_creation_failed")
			return
		}

		// 重定向到 service 并携带 ticket
		redirectURL := service
		if service != "" {
			if strings.Contains(service, "?") {
				redirectURL += "&ticket=" + st.ID
			} else {
				redirectURL += "?ticket=" + st.ID
			}
		}

		c.base.Logger.Info("OAuth login successful",
			zap.String("provider", string(provider)),
			zap.String("user_id", user.ID),
			zap.String("email", user.Email))

		ctx.Redirect(http.StatusFound, redirectURL)
	}
}

// redirectToProfileWithSuccess 重定向到个人资料页面并显示成功消息
func (c *OAuthController) redirectToProfileWithSuccess(ctx *gin.Context) {
	url := location.Get(ctx)
	if url == nil {
		// 如果无法获取 URL，使用默认的个人资料页面
		profileURL := "/cas/profile?oauth_success=true"
		ctx.Redirect(http.StatusFound, profileURL)
		return
	}
	profileURL := url.Scheme + "://" + url.Host + "/cas/profile?oauth_success=true"
	ctx.Redirect(http.StatusFound, profileURL)
}

// redirectToProfileWithError 重定向到个人资料页面并显示错误消息
func (c *OAuthController) redirectToProfileWithError(ctx *gin.Context, errorCode string) {
	url := location.Get(ctx)
	if url == nil {
		// 如果无法获取 URL，使用默认的个人资料页面
		profileURL := "/cas/profile?oauth_error=" + errorCode
		ctx.Redirect(http.StatusFound, profileURL)
		return
	}
	profileURL := url.Scheme + "://" + url.Host + "/cas/profile?oauth_error=" + errorCode
	ctx.Redirect(http.StatusFound, profileURL)
}

// redirectToError 重定向到错误页面
func (c *OAuthController) redirectToError(ctx *gin.Context, errorCode string) {
	url := location.Get(ctx)
	if url == nil {
		// 如果无法获取 URL，使用默认的错误页面
		errorURL := "/cas/error?code=" + errorCode
		ctx.Redirect(http.StatusFound, errorURL)
		return
	}
	errorURL := url.Scheme + "://" + url.Host + "/cas/error?code=" + errorCode
	ctx.Redirect(http.StatusFound, errorURL)
}

// GetOAuthProviders 获取 OAuth 提供商列表
//
//	@Summary		Get OAuth Providers
//	@Description	Get list of available OAuth providers
//	@Tags			oauth
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Success		200	{object}	model.OAuthProvidersResponse	"OAuth providers list"
//	@Router			/api/oauth/providers [get]
func (c *OAuthController) GetOAuthProviders(ctx *gin.Context) {
	// 从 token 中获取用户 ID
	userID, exists := ctx.Get("userId")
	if !exists {
		response.Error[any](ctx, response.UNAUTHORIZED, c.base.I18n.T("Invalid user ID in token"), nil)
		return
	}

	providers, err := c.service.OAuthService.GetUserOAuthProviders(ctx, userID.(string))
	if err != nil {
		c.base.Logger.Error("Failed to get OAuth providers", zap.Error(err))
		response.Error[any](ctx, response.ERROR, c.base.I18n.T("Failed to get OAuth providers"), nil)
		return
	}

	response.Success(ctx, c.base.I18n.T("Success"), providers)
}

// UnlinkOAuthProvider 解绑 OAuth 提供商
//
//	@Summary		Unlink OAuth Provider
//	@Description	Unlink OAuth provider from user account
//	@Tags			oauth
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			request	body		model.OAuthUnlinkRequest	true	"OAuth unlink params"
//	@Success		200		{object}	model.OAuthUnlinkResponse	"OAuth provider unlinked"
//	@Router			/api/oauth/unlink [post]
func (c *OAuthController) UnlinkOAuthProvider(ctx *gin.Context) {
	// 从 token 中获取用户 ID
	userID, exists := ctx.Get("userId")
	if !exists {
		response.Error[any](ctx, response.UNAUTHORIZED, c.base.I18n.T("Invalid user ID in token"), nil)
		return
	}

	var req model.OAuthUnlinkRequest
	if err := ctx.ShouldBindJSON(&req); err != nil {
		response.Error[any](ctx, response.INVALID_PARAMS, c.base.I18n.T("Invalid parameters"), nil)
		return
	}

	// 解绑 OAuth 提供商
	err := c.service.OAuthService.UnlinkOAuthProvider(ctx, userID.(string), req.Provider)
	if err != nil {
		c.base.Logger.Error("Failed to unlink OAuth provider", zap.Error(err))
		response.Error[any](ctx, response.ERROR, c.base.I18n.T("Failed to unlink OAuth provider"), nil)
		return
	}

	responseData := model.OAuthUnlinkResponse{
		Response: model.Response{
			Code:    response.SUCCESS,
			Message: c.base.I18n.T("OAuth provider unlinked successfully"),
		},
		Data: struct {
			Unlinked bool `json:"unlinked"`
		}{
			Unlinked: true,
		},
	}

	response.Success(ctx, c.base.I18n.T("OAuth provider unlinked successfully"), responseData)
}

// SetPrimaryEmail 设置 OAuth 邮箱为主邮箱
//
//	@Summary		Set Primary Email from OAuth
//	@Description	Set OAuth email as primary email
//	@Tags			oauth
//	@Accept			json
//	@Produce		json
//	@Security		BearerAuth
//	@Param			request	body		model.OAuthSetPrimaryEmailRequest	true	"Set primary email params"
//	@Success		200		{object}	model.Response	"Primary email updated"
//	@Router			/api/oauth/set-primary-email [post]
func (c *OAuthController) SetPrimaryEmail(ctx *gin.Context) {
	// 从 token 中获取用户 ID
	userID, exists := ctx.Get("userId")
	if !exists {
		response.Error[any](ctx, response.UNAUTHORIZED, c.base.I18n.T("Invalid user ID in token"), nil)
		return
	}

	var req model.OAuthSetPrimaryEmailRequest
	if err := ctx.ShouldBindJSON(&req); err != nil {
		response.Error[any](ctx, response.INVALID_PARAMS, c.base.I18n.T("Invalid parameters"), nil)
		return
	}

	// 设置主邮箱
	err := c.service.OAuthService.SetPrimaryEmail(ctx, userID.(string), req.Provider)
	if err != nil {
		c.base.Logger.Error("Failed to set primary email",
			zap.String("provider", string(req.Provider)),
			zap.String("user_id", userID.(string)),
			zap.Error(err))
		response.Error[any](ctx, response.ERROR, c.base.I18n.T("Failed to set primary email"), nil)
		return
	}

	response.Success[any](ctx, c.base.I18n.T("Primary email updated successfully"), nil)
}
